#include "main_msp.h"
#include "includes.h"

enum PLLClkSrc {
    PLL_HSI = 0,
    PLL_HSE,
    PLL_end
};

typedef struct ClockConfigStruct
{
    uint32_t Sysclk;
    uint32_t APB1_Div;
    uint32_t APB2_Div;
    uint32_t Mult;
    uint32_t PllRange;
} ClkConfStruct;

/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/


const ClkConfStruct ClockTable[] =
{
    {24, RCC_CFG_APB1PSC_DIV1, RCC_CFG_APB2PSC_DIV1, RCC_CFG_PLLMULT6, RCC_CFG_PLLRANGE_LE72MHZ},
    {36, RCC_CFG_APB1PSC_DIV1, RCC_CFG_APB2PSC_DIV1, RCC_CFG_PLLMULT9, RCC_CFG_PLLRANGE_LE72MHZ},
    {48, RCC_CFG_APB1PSC_DIV1, RCC_CFG_APB2PSC_DIV1, RCC_CFG_PLLMULT12, RCC_CFG_PLLRANGE_LE72MHZ},
    {56, RCC_CFG_APB1PSC_DIV1, RCC_CFG_APB2PSC_DIV1, RCC_CFG_PLLMULT14, RCC_CFG_PLLRANGE_LE72MHZ},
    {72, RCC_CFG_APB1PSC_DIV1, RCC_CFG_APB2PSC_DIV1, RCC_CFG_PLLMULT18, RCC_CFG_PLLRANGE_LE72MHZ},
    {96, RCC_CFG_APB1PSC_DIV2, RCC_CFG_APB2PSC_DIV2, RCC_CFG_PLLMULT24, RCC_CFG_PLLRANGE_GT72MHZ},
    {108, RCC_CFG_APB1PSC_DIV2, RCC_CFG_APB2PSC_DIV2, RCC_CFG_PLLMULT27, RCC_CFG_PLLRANGE_GT72MHZ},
    {120, RCC_CFG_APB1PSC_DIV2, RCC_CFG_APB2PSC_DIV2, RCC_CFG_PLLMULT30, RCC_CFG_PLLRANGE_GT72MHZ},
    {144, RCC_CFG_APB1PSC_DIV2, RCC_CFG_APB2PSC_DIV2, RCC_CFG_PLLMULT36, RCC_CFG_PLLRANGE_GT72MHZ},
#if defined (AT32F403xx) || defined (AT32F413xx) || \
    defined (AT32F403Axx)|| defined (AT32F407xx)
    {168, RCC_CFG_APB1PSC_DIV2, RCC_CFG_APB2PSC_DIV2, RCC_CFG_PLLMULT42, RCC_CFG_PLLRANGE_GT72MHZ},
    {176, RCC_CFG_APB1PSC_DIV2, RCC_CFG_APB2PSC_DIV2, RCC_CFG_PLLMULT44, RCC_CFG_PLLRANGE_GT72MHZ},
    {192, RCC_CFG_APB1PSC_DIV2, RCC_CFG_APB2PSC_DIV2, RCC_CFG_PLLMULT48, RCC_CFG_PLLRANGE_GT72MHZ},
    {200, RCC_CFG_APB1PSC_DIV2, RCC_CFG_APB2PSC_DIV2, RCC_CFG_PLLMULT50, RCC_CFG_PLLRANGE_GT72MHZ},
#endif
#if defined (AT32F403Axx) || defined (AT32F407xx)
    {224, RCC_CFG_APB1PSC_DIV2, RCC_CFG_APB2PSC_DIV2, RCC_CFG_PLLMULT56, RCC_CFG_PLLRANGE_GT72MHZ},
    {240, RCC_CFG_APB1PSC_DIV2, RCC_CFG_APB2PSC_DIV2, RCC_CFG_PLLMULT60, RCC_CFG_PLLRANGE_GT72MHZ}
#endif
};

/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

#ifdef AT32F403xx
/**
  * @brief  Delay to wait for HSE stable.
  * @note   This function should be used before reading the HSESTBL flag.
  * @param  None
  * @retval None
  */
static void WaitHseStbl(uint32_t delay)
{
    uint32_t i;

    for(i = 0; i < delay; i++)
        ;
}
#endif

/**
  * @brief  Search the destination frequency program
  * @param  DestClkFreq:
  * @retval success return index, error return (-1)
  */
int32_t SearchIndex(uint32_t DestClkFreq)
{
    int32_t index = 0;
    for(index = 0; index < sizeof(ClockTable)/sizeof(ClkConfStruct); index ++)
    {
        if(ClockTable[index].Sysclk == DestClkFreq)
        {
            return index;
        }
    }
    return -1;
}

/**
  * @brief  Clock reset program
  * @param  None
  * @retval None
  */
void ClockReset(void)
{
    /* Reset the RCC clock configuration to the default reset state(for debug purpose) */
    /* Set HSIEN bit */
    BIT_SET(RCC->CTRL, RCC_CTRL_HSIEN);

    /* Reset SW, AHBPSC, APB1PSC, APB2PSC, ADCPSC and CLKOUT bits */
    BIT_CLEAR(RCC->CFG, RCC_CFG_SYSCLKSEL | RCC_CFG_AHBPSC | \
              RCC_CFG_APB1PSC | RCC_CFG_APB2PSC | \
              RCC_CFG_ADCPSC | RCC_CFG_CLKOUT);

    /* Reset HSEEN, HSECFDEN and PLLEN bits */
    BIT_CLEAR(RCC->CTRL, RCC_CTRL_HSEEN | RCC_CTRL_HSECFDEN | \
              RCC_CTRL_PLLEN);

    /* Reset HSEBYPS bit */
    BIT_CLEAR(RCC->CTRL, RCC_CTRL_HSEBYPS);

    /* Reset PLLRC, PLLHSEPSC, PLLMUL, USBPSC and PLLRANGE bits */
    BIT_CLEAR(RCC->CFG, RCC_CFG_PLLRC | RCC_CFG_PLLHSEPSC | \
              RCC_CFG_PLLMULT | RCC_CFG_USBPSC | RCC_CFG_PLLRANGE);

    /* Reset USB768B, CLKOUT[3], HSICAL_KEY[7:0] */
    BIT_CLEAR(RCC->MISC, 0x010100FF);

    /* Disable all interrupts and clear pending bits  */
    RCC->CLKINT = RCC_CLKINT_LSISTBLFC | RCC_CLKINT_LSESTBLFC | \
                  RCC_CLKINT_HSISTBLFC | RCC_CLKINT_HSESTBLFC | \
                  RCC_CLKINT_PLLSTBLFC | RCC_CLKINT_HSECFDFC;
}

/**
  * @brief  Sysclk configuration program
  * @param  Src : PLL source clock (HSE or HSI).
  *         DestClkFreq: Destination clock frequency
  * @retval ErrorStatus: ERROR or SUCCESS
  */
ErrorStatus SysclkConfig(enum PLLClkSrc Src, uint32_t DestClkFreq)
{
    __IO uint32_t StartUpCounter = 0, Stabled = 0;
    __IO int32_t index = 0;

    /* Check the DestClkFreq*/
    index = SearchIndex(DestClkFreq);
    if(index == -1)
    {
        return ERROR;
    }

    /* Reset all RCC configuration */
    ClockReset();

    /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/
    if(Src == PLL_HSE)
    {
        /* Enable HSE */
        RCC->CTRL |= ((uint32_t)RCC_CTRL_HSEEN);
        /* Wait till HSE is ready and if Time out is reached exit */
        do
        {
            Stabled = RCC->CTRL & RCC_CTRL_HSESTBL;
            StartUpCounter++;
        }
        while((Stabled == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
#ifdef AT32F403xx
        WaitHseStbl(HSE_STABLE_DELAY);
#endif
        if ((RCC->CTRL & RCC_CTRL_HSESTBL) != RESET)
        {
            Stabled = (uint32_t)0x01;
        }
    }
    else if(Src == PLL_HSI)
    {
        /* Enable HSI */
        RCC->CTRL |= ((uint32_t)RCC_CTRL_HSIEN);
        /* Wait till HSI is ready and if Time out is reached exit */
        do
        {
            Stabled = RCC->CTRL & RCC_CTRL_HSISTBL;
            StartUpCounter++;
        }
        while((Stabled == 0) && (StartUpCounter != 0xFFFF));
        if ((RCC->CTRL & RCC_CTRL_HSISTBL) != RESET)
        {
            Stabled = (uint32_t)0x01;
        }
    }

    if (Stabled == (uint32_t)0x01)
    {
        /* HCLK = SYSCLK */
        RCC->CFG |= (uint32_t)RCC_CFG_AHBPSC_DIV1;

        /* PCLK2 = HCLK/2 */
        RCC->CFG &= 0xFFFFC7FF;
        RCC->CFG |= (uint32_t)ClockTable[index].APB2_Div;

        /* PCLK1 = HCLK/2 */
        RCC->CFG &= 0xFFFFF8FF;
        RCC->CFG |= (uint32_t)ClockTable[index].APB1_Div;

        /*  PLL configuration */
        RCC->CFG &= RCC_CFG_PLLCFG_MASK;

        if(Src == PLL_HSE)
        {
            RCC->CFG |= (uint32_t)(RCC_CFG_PLLRC_HSE | RCC_CFG_PLLHSEPSC_HSE_DIV2 | ClockTable[index].Mult | ClockTable[index].PllRange);
        } else if(Src == PLL_HSI)
        {
            RCC->CFG |= (uint32_t)(RCC_CFG_PLLRC_HSI_DIV2 | ClockTable[index].Mult | ClockTable[index].PllRange);
        }

        /* Enable PLL */
        RCC->CTRL |= RCC_CTRL_PLLEN;

        /* Wait till PLL is ready */
        while((RCC->CTRL & RCC_CTRL_PLLSTBL) == 0)
        {
        }
#if defined (AT32F413xx) || defined (AT32F403Axx)|| \
    defined (AT32F407xx)
        RCC_StepModeCmd(ENABLE);
#endif
        /* Select PLL as system clock source */
        RCC->CFG &= (uint32_t)((uint32_t)~(RCC_CFG_SYSCLKSEL));
        RCC->CFG |= (uint32_t)RCC_CFG_SYSCLKSEL_PLL;

        /* Wait till PLL is used as system clock source */
        while ((RCC->CFG & (uint32_t)RCC_CFG_SYSCLKSTS) != RCC_CFG_SYSCLKSTS_PLL)
        {
        }
#ifdef AT32F403xx
        WaitHseStbl(PLL_STABLE_DELAY);
#endif
#if defined (AT32F413xx) || defined (AT32F403Axx)|| \
    defined (AT32F407xx)
        RCC_StepModeCmd(DISABLE);
#endif
    }

    /* Update system clock */
    SystemCoreClockUpdate();

    return SUCCESS;
}

/**
  * @brief  MCO configuration program
  * @param  None
  * @retval None
  */
void MCO_config(void)
{
    GPIO_InitType GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2PERIPH_GPIOA,ENABLE);
    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pins = GPIO_Pins_8;
    GPIO_InitStructure.GPIO_MaxSpeed = GPIO_MaxSpeed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    RCC_CLKOUTConfig(RCC_CLKOUT_SYSCLK, RCC_MCOPRE_2);
}


//typedef enum{
//	AHB_DIV_1=RCC_CFG_AHBPSC_DIV1,
//	AHB_DIV_2=RCC_CFG_AHBPSC_DIV2,
//	AHB_DIV_4=RCC_CFG_AHBPSC_DIV4,
//	AHB_DIV_8=RCC_CFG_AHBPSC_DIV8,
//	AHB_DIV_16=RCC_CFG_AHBPSC_DIV16,
//	AHB_DIV_64=RCC_CFG_AHBPSC_DIV64,
//	AHB_DIV_128=RCC_CFG_AHBPSC_DIV128,
//	AHB_DIV_256=RCC_CFG_AHBPSC_DIV256,
//	AHB_DIV_512=RCC_CFG_AHBPSC_DIV512
//}AHB_DIV_T;

//typedef enum{
//	APB2_DIV_1=RCC_CFG_APB2PSC_DIV1,	//               ((uint32_t)0x00000000)        /*!< HCLK not divided */
//	APB2_DIV_2=RCC_CFG_APB2PSC_DIV2, 	//               ((uint32_t)0x00002000)        /*!< HCLK divided by 2 */
//	APB2_DIV_4=RCC_CFG_APB2PSC_DIV4,    //               ((uint32_t)0x00002800)        /*!< HCLK divided by 4 */
//	APB2_DIV_8=RCC_CFG_APB2PSC_DIV8,    //               ((uint32_t)0x00003000)        /*!< HCLK divided by 8 */
//	APB2_DIV_16=RCC_CFG_APB2PSC_DIV16   //               ((uint32_t)0x00003800)        /*!< HCLK divided by 16 */
//}APB2_DIV_T;


//typedef enum{
//	APB1_DIV_1=RCC_CFG_APB1PSC_DIV1,	//               ((uint32_t)0x00000000)        /*!< HCLK not divided */
//	APB1_DIV_2=RCC_CFG_APB1PSC_DIV2,	//               ((uint32_t)0x00000400)        /*!< HCLK divided by 2 */
//	APB1_DIV_4=RCC_CFG_APB1PSC_DIV4,	//               ((uint32_t)0x00000500)        /*!< HCLK divided by 4 */
//	APB1_DIV_8=RCC_CFG_APB1PSC_DIV8,	//               ((uint32_t)0x00000600)        /*!< HCLK divided by 8 */
//	APB1_DIV_16=RCC_CFG_APB1PSC_DIV16,	//               ((uint32_t)0x00000700)        /*!< HCLK divided by 16 */
//}APB1_DIV_T;


//void BUS_Clock_Config(AHB_DIV_T AHB_div, APB2_DIV_T APB2_Div, APB1_DIV_T APB1_Div)
//{
//	/* HCLK = SYSCLK */
//    RCC->CFG |= (uint32_t)AHB_div;

//    /* PCLK2 = HCLK/2 */
//    RCC->CFG &= 0xFFFFC7FF;
//    RCC->CFG |= (uint32_t)APB2_Div;

//    /* PCLK1 = HCLK/2 */
//    RCC->CFG &= 0xFFFFF8FF;
//    RCC->CFG |= (uint32_t)APB1_Div;
//}


void GPIO_TogglePin(GPIO_Type* GPIOx, uint16_t GPIO_Pin)
{
    uint32_t odr;
    /* get current Ouput Data Register value */
    odr = GPIOx->OPTDT;

    /* Set selected pins that were at low level, and reset ones that were high */
    GPIOx->BSRE = ((odr & GPIO_Pin) << 16) | (~odr & GPIO_Pin);
}


void Target_GPIO_Init(void)
{
    /*
    GPIO_InitType GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2PERIPH_GPIOC , ENABLE);

    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT_PP;
    GPIO_InitStructure.GPIO_MaxSpeed=GPIO_MaxSpeed_2MHz;
    GPIO_InitStructure.GPIO_Pins = LED1_PIN | LED2_PIN;
    GPIO_Init(LED1_PORT, &GPIO_InitStructure);
    */
}


void Target_ReaetInit(void)
{
    NVIC_SetPriorityGrouping(3);	//4.4
    //SysclkConfig(PLL_HSI,120);	//120M
    SysclkConfig(PLL_HSE,SYS_FREQ/1000000);	//200M
    //SysclkConfig(PLL_HSE,72);
    //MCO_config();
    SysTick_Config(SystemCoreClock/1000);
    RCC_APB2PeriphClockCmd(RCC_APB2PERIPH_GPIOA | RCC_APB2PERIPH_GPIOB | RCC_APB2PERIPH_AFIO, ENABLE);
    GPIO_PinsRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
    Target_GPIO_Init();
}
void extend_SRAM(void)
{
/* Target set_SRAM_16K is selected */
#ifdef EXTEND_SRAM_16K
  // check if RAM has been set to 16K, if not, change EOPB0
  if(((UOPTB->EOPB0)&0x03)!=0x01)
  {
    /* Unlock Option Bytes Program Erase controller */
    FLASH_Unlock();
    /* Erase Option Bytes */
    FLASH_EraseUserOptionBytes();
    /* Change SRAM size to 16KB */
    FLASH_ProgramUserOptionByteData((uint32_t)&UOPTB->EOPB0,0xFD);
    NVIC_SystemReset();
  }
#endif

/* Target set_SRAM_32K is selected */
#ifdef EXTEND_SRAM_32K
  // check if RAM has been set to 32K, if not, change EOPB0
  if(((UOPTB->EOPB0)&0x03)!=0x03)
  {
    /* Unlock Option Bytes Program Erase controller */
    FLASH_Unlock();
    /* Erase Option Bytes */
    FLASH_EraseUserOptionBytes();
    /* Change SRAM size to 32KB */
    FLASH_ProgramUserOptionByteData((uint32_t)&UOPTB->EOPB0,0xFF);
    NVIC_SystemReset();
  }
 #endif

/* Target set_SRAM_64K is selected */
#ifdef EXTEND_SRAM_64K
  // check if RAM has been set to 64K, if not, change EOPB0
  if(((UOPTB->EOPB0)&0x01))  
  {
    /* Unlock Option Bytes Program Erase controller */
    FLASH_Unlock();
    /* Erase Option Bytes */
    FLASH_EraseUserOptionBytes();
    /* Change SRAM size to 64KB */
    FLASH_ProgramUserOptionByteData((uint32_t)&UOPTB->EOPB0,0xFC);
    NVIC_SystemReset();
  }
#endif

/* Target set_SRAM_224K is selected */
#ifdef EXTEND_SRAM_224K
/* check if RAM has been set to 224K, if not, change EOPB0 */
  if(((UOPTB->EOPB0)&0xFF)!=0xFE)
  {
    /* Unlock Option Bytes Program Erase controller */
    FLASH_Unlock();
    /* Erase Option Bytes */
    FLASH_EraseUserOptionBytes();
    /* Change SRAM size to 224KB */
    FLASH_ProgramUserOptionByteData((uint32_t)&UOPTB->EOPB0,0xFE);
    NVIC_SystemReset();
  }
#endif
}
void show_using_sram_size(void){
#ifdef EXTEND_SRAM_16K

  if(((UOPTB->EOPB0)&0x03)==0x01)
  {
  	  printf("Extend 16KB SRAM success.\r\n");
  } else {
  	  printf("Extend 16KB SRAM failed.\r\n");
  }		  
#endif

#ifdef EXTEND_SRAM_32K

  if(((UOPTB->EOPB0)&0x03)==0x03)
  {
  	  printf("Extend 32KB SRAM success.\r\n");
  } else {
  	  printf("Extend 32KB SRAM failed.\r\n");
  }		  
#endif

#ifdef EXTEND_SRAM_64K

  if(!((UOPTB->EOPB0)&0x01))
  {
  	 printf("Extend 64KB SRAM success.\r\n");
  } else {
  	 printf("Extend 64KB SRAM failed.\r\n");
  }		  
#endif
#ifdef EXTEND_SRAM_224K

  if(((UOPTB->EOPB0)&0xFF)==0xFE)
  {
  	  printf("Extend 224KB SRAM success.\r\n");
  } else {
  	  printf("Extend 224KB SRAM failed.\r\n");
  }		  
#endif
}
